/**
 * session认证的局限性：
 *   session是基于cookie实现的，cookie默认不支持跨域，因此当涉及到前端跨域请求接口的时候需要做很多的配置
 * jwt认证： json web token
 * 工作原理：用户登录成功后，服务器端生成一个token返回给客户端，客户端将token存储在本地，下次请求时携带token，服务器端验证token是否合法
 * 组成：header.payload.signature
 * payload：存储用户信息,是用户信息经过加密后的字符串
 * signature：签名，由header和payload通过秘钥生成的，只是为了保证token的安全性
 */

/**
 * jwt的使用：
 * 1. 安装jsonwebtoken express-jwt
 *      npm install jsonwebtoken express-jwt
 *      jsonwebtoken: 用来生成jwt字符串
 *      express-jwt: 用来验证jwt字符串解析还原成JSON对象
 */
const express = require('express')
const app = express()
const bodyParser = require('body-parser')

const jwt = require('jsonwebtoken')
const { expressjwt } = require('express-jwt');
/**
 * 定义secret密钥: 为了保证 JWT 字符串的安全性，防止 JWT 字符串在网络传输过程中被别人破解，我们需要专门定义一个用于加密和解密的secret 密钥
 *   1. secret 密钥是一个字符串，可以是任意字符串，但是一般情况下我们会定义一个比较长的字符串
 *   2.当生成jwt字符串的时候，需要使用secret密钥进行加密，最终生成一个jwt字符串
 *   3.当解析jwt字符串的时候，需要使用secret密钥进行解密
 */

const secretKey = 'itheima No1 🤍'
/**
 * express中使用jwt：
 *  1. 调用 jsonwebtoken 包提供的 sign() 方法，将用户的信息加密成 JWT 字符串，响应给客户端
 *     expiresIn: 有效期，单位是秒
 */
app.use(bodyParser.json())

/**
 * 将jwt字符串解析成JSON对象
 * 1. 客户端每次在访问受保护的接口时，需要住的通过请求头中的Authorization字段携带token字符串送到服务器进行身份验证
 * 2. expressJwt()是一个中间件，用来自动将客户端发送过来的token字符串解析成JSON对象，然后挂载到req.user上
 * 
 * unless: 用来排除某些接口不需要进行token验证
 */
app.use(expressjwt({ secret: secretKey, algorithms: ["HS256"]}).unless({ path: ['/login'] }));

app.use((req, res,next) => {
    res.setHeader('Access-Control-Allow-Origin', '*')
    res.setHeader('Access-Control-Allow-Headers', '*')
    res.setHeader('Access-Control-Allow-Methods', '*')
    next()
})
app.use((err, req, res, next) => {
    console.log('err:>>> ', err)
    if (err.name === 'UnauthorizedError') {
        return res.send({status: 401, message: 'invalid token...'})
    }
    res.send({status: 500, message: '未知错误'})
})

app.post('/login', (req,res) => {
    let { username, password } = req.body
    res.send({
        code: 0,
        message: '登录成功',
        token: jwt.sign({ username, password }, secretKey, { expiresIn: 60 * 60 * 24 })
    })
})

app.get('/admin', (req,res) => {
    if (!req.auth) {
        res.status(401).send('invalid token...')
        return
    }
    res.send({
        code: 0,
        message: '获取管理员信息成功',
        data: req.auth
    })
})

app.listen(8080, () => {
    console.log('服务启动成功', 'http://localhost:8080')
})